package logger

import (
	"os"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

// NewZapLogger creates a new zap logger instance based on configuration
func NewZapLogger(cfg *Config) (*zap.Logger, error) {
	// Validate configuration
	if err := cfg.Validate(); err != nil {
		return nil, err
	}

	// Parse log level
	var level zapcore.Level
	if err := level.UnmarshalText([]byte(cfg.Level)); err != nil {
		level = zapcore.InfoLevel
	}

	var cores []zapcore.Core

	// File output configuration
	if cfg.Path != "" {
		fileCore := createFileCore(cfg, level)
		cores = append(cores, fileCore)
	}

	// Console output configuration
	if cfg.ConsoleEnabled {
		consoleCore := createConsoleCore(cfg, level)
		cores = append(cores, consoleCore)
	}

	// If no output is configured, default to console
	if len(cores) == 0 {
		consoleCore := createConsoleCore(cfg, level)
		cores = append(cores, consoleCore)
	}

	// Combine multiple cores
	core := zapcore.NewTee(cores...)

	// Configure options
	options := buildZapOptions(cfg)

	return zap.New(core, options...), nil
}

// createFileCore creates a core for file output
func createFileCore(cfg *Config, level zapcore.Level) zapcore.Core {
	// Lumberjack log rotation
	lj := &lumberjack.Logger{
		Filename:   cfg.Path,
		MaxSize:    cfg.MaxSize,
		MaxBackups: cfg.MaxBackups,
		MaxAge:     cfg.MaxAge,
		Compress:   true,
		LocalTime:  true,
	}

	// Create encoder based on format
	encoder := createFileEncoder(cfg.Format)

	return zapcore.NewCore(
		encoder,
		zapcore.AddSync(lj),
		zap.NewAtomicLevelAt(level),
	)
}

// createConsoleCore creates a core for console output
func createConsoleCore(cfg *Config, level zapcore.Level) zapcore.Core {
	// Create encoder based on format
	encoder := createConsoleEncoder(cfg.Format)

	return zapcore.NewCore(
		encoder,
		zapcore.AddSync(os.Stdout),
		zap.NewAtomicLevelAt(level),
	)
}

// createFileEncoder creates encoder for file output (no colors)
func createFileEncoder(format string) zapcore.Encoder {
	switch format {
	case "json":
		return zapcore.NewJSONEncoder(getJSONEncoderConfig())
	case "console":
		return zapcore.NewConsoleEncoder(getFileConsoleEncoderConfig())
	default:
		return zapcore.NewConsoleEncoder(getFileConsoleEncoderConfig())
	}
}

// createConsoleEncoder creates encoder for console output (with colors)
func createConsoleEncoder(format string) zapcore.Encoder {
	switch format {
	case "json":
		return zapcore.NewJSONEncoder(getJSONEncoderConfig())
	case "console":
		return zapcore.NewConsoleEncoder(getConsoleEncoderConfig())
	default:
		return zapcore.NewConsoleEncoder(getConsoleEncoderConfig())
	}
}

// getFileConsoleEncoderConfig gets console encoder configuration for file output (no colors)
func getFileConsoleEncoderConfig() zapcore.EncoderConfig {
	return zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		FunctionKey:    zapcore.OmitKey,
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,
		EncodeTime:     zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000"),
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
}

// getJSONEncoderConfig gets JSON encoder configuration
func getJSONEncoderConfig() zapcore.EncoderConfig {
	return zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		FunctionKey:    zapcore.OmitKey,
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.LowercaseLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
}

// getConsoleEncoderConfig gets console encoder configuration
func getConsoleEncoderConfig() zapcore.EncoderConfig {
	return zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		FunctionKey:    zapcore.OmitKey,
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalColorLevelEncoder,
		EncodeTime:     zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000"),
		EncodeDuration: zapcore.StringDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}
}

// buildZapOptions builds zap options based on configuration
func buildZapOptions(cfg *Config) []zap.Option {
	var options []zap.Option

	if cfg.EnableCaller {
		options = append(options, zap.AddCaller())
	}

	if cfg.EnableStacktrace {
		options = append(options, zap.AddStacktrace(zapcore.ErrorLevel))
	}

	return options
}
